package world.edges;

import exceptions.IllegalSquarePlacementException;
import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Raw;
import world.placement.Direction;
import world.square.Square;


/**
 * Class representing the border of a square
 * 
 * @invar	The bordering square is valid.
 * 		  	| isProperSquare(getSquare())
 * @invar 	The border element is valid.
 * 		  	| isValidBorderElement(getBorderElement())
 * @invar 	The direction of the bordering square is valid.
 * 		  	| isValidDirection(getDirection)
 * @author 	Wim Marynissen, Frederic Mes
 * @version 1.0
 */
public class Border {
	/**
     * Initialize a new border
     * 
     * @param   borderElement
     * 			The borderelement in this border.
     * @param   direction
     *          The direction of the square relative to this border.
     * @post	The borderelement is initialised.
     * 			| new.getBorderElement() == borderElement
     * @post	The direction is initialised.
     * 			| new.getDirection() == direction
     * @throws	IllegalArgumentException
     * 			The specified borderElement is invalid.
     * 			| !isValidBorderElement(borderElement)
     * @throws	IllegalArgumentException
     * 			The specified direction is invalid.
     * 			| !isValidDirection(direction)
     */
	public Border (BorderElement borderElement, Direction direction) throws IllegalArgumentException{
		setDirection (direction);
		setBorderElement (borderElement);	
	}
	
	/**
	 * @param 	borderElement
	 * 			The new bordering element.
	 * @post	The new borderelement is equal to the provided object
	 * 			| new.getBorderElement() == borderElement
	 * @throws	IllegalArgumentException
	 * 			The provided border element is invalid.
	 * 			| !isValidBorderElement(borderElement)
	 */
	@Raw
	public void setBorderElement(BorderElement borderElement) throws IllegalArgumentException{
		if(!isValidBorderElement(borderElement))
			throw new IllegalArgumentException("Invalid border element.");
		this.borderelement = borderElement;
	}
	
	/**
	 * @param 	direction
	 * 			The new direction of the bordering square.
	 * @post	The new direction is equal to the one specified.
	 * 			| new.getDirection() == direction
	 * @throws	IllegalArgumentException
	 * 			The provided direction is invalid.
	 * 			| !isValidDirection(direction)
	 */
	@Raw
	public void setDirection (Direction direction){
		if(!isValidDirection(direction))
			throw new IllegalArgumentException("Invalid direction.");
		this.direction = direction;
	}
	
	/**
	 * Returns the direction of the bordering square.	
	 */
	@Basic @Raw
	public Direction getDirection (){
		return this.direction;
	}
	
	/**
	 * Returns the border element at this border.	
	 */
	@Basic @Raw
	public BorderElement getBorderElement(){
		return this.borderelement;
	}
	
	/**
	 * Variable referencing the bordering element or obstacle.
	 */
	private BorderElement borderelement;
	
	/**
	 * Variable referencing the direction of the bordering square
	 */
	private Direction direction;
	
	/**
     * Check is the border element is a valid one.
     * @param	borderElement
     * 			The border element to validate.
     * @return 	True if the border element is effective
     *          | result == (border != null)
     */
	public static boolean isValidBorderElement(BorderElement borderElement){
		return (borderElement != null);
	}
	
	/**
     * Check if the specified direction is valid.
     * @param	direction
     * 			The direction to validate.
     * @return 	True if the direction is effective
     *          | result == (direction != null)
     */
	public static boolean isValidDirection (Direction direction){
		return (direction != null);
	}
	
	/**
	 * @param 	square
	 * 			The square to add to this border.
	 * @throws 	IllegalSquarePlacementException
	 * 			The specified square does not refer to this border in the required direction.
	 * 			| square.getBorder(getDirection().getOpposite()) != this
	 */
	@Raw
	public void setSquare(Square square) throws IllegalSquarePlacementException{ 
		if(!isProperSquare(square))
			throw new IllegalSquarePlacementException();
		this.square = square;
	}
	
	/**
	 * Check whether this border has a proper square.
	 *
	 * @return  True if the square is effective,
	 * 			and refers back to this border.
	 * 			| result == (square != null)
	 * 			|				&& (square.getBorder(getDirection().getOpposite()) == this)
	 */
	@Raw
	public boolean isProperSquare(Square square) {
		return ((square != null) && (square.getBorder(getDirection()) == this));
	}

	
	/**
     * Remove the bordering square in a given direction.
     * 
     * @param   direction
     *          The direction in which the square must be removed.
     * @post    This border no longer references the bordering square
     *          | new.getSquare() == null
     */
	public void removeSquare(){
		square = null;
	}
	
	/**
	 * Returns the bordering square.
	 */
	@Basic @Raw
	public Square getSquare (){
		return this.square;
	}
	
	/**
	 * Variable referring to the square next to this border.
	 */
	private Square square = null;
	
	
	/**
	 * Get thesame border, with a square in the opposite direction
	 * @return	Border with thesame element as this border, but opposite direction and a different square.
	 */
	public Border oppositeBorder(){ 
		return new Border (getBorderElement(), this.getDirection().getOpposite());
	}	

    /**
     * Terminate the border.
     * 
     * @post This border is terminated.
     *       | new.isTerminated()
     * @post If this border was not already terminated, this border no longer has bordering squares.
     *       | for each direction in Direction:
     *       |       if( !isTerminated() )
     *       |               then new.hasSquareInDirection(direction) == false
     * @post If this border was not already terminated, the bordering squares 
     *       no longer have this border as their border.
     *       | for each direction in Direction:
     *       |  	if( !isTerminated() )
     *       |      	then (!(new getSquareInDirection(direction))
     *       |                 	.hasBorderInDirection(direction)
     */
    public void terminate() {
            if(!isTerminated()){
            	this.square = null;
              	this.isTerminated = true;
            }
    }
    
    /**
     * Check whether this border is terminated.
     */
    @Basic @Raw
    public boolean isTerminated() {
            return this.isTerminated;
    } 
	
	/**
     * Variable registering whether this border is terminated.
     */
    private boolean isTerminated = false;

}
